home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr05
/
xnot12a.zip
/
LINE.C
< prev
next >
Wrap
C/C++ Source or Header
|
1993-05-20
|
19KB
|
801 lines
#include "jam.h"
#include"stdlib.h"
#include "def.h"
#include "keyname.h"
/*
* Text line handling.
* The functions in this file
* are a general set of line management
* utilities. They are the only routines that
* touch the text. They also touch the buffer
* and window structures, to make sure that the
* necessary updating gets done. There are routines
* in this file that handle the kill buffer too.
* It isn't here for any good reason.
*
* Note that this code only updates the dot and
* mark values in the window list. Since all the code
* acts on the current window, the buffer that we
* are editing must be being displayed, which means
* that "b_nwnd" is non zero, which means that the
* dot and mark values in the buffer headers are
* nonsense.
*/
#ifndef NBLOCK
# define NBLOCK 16 /* Line block chunk size */
#endif
#ifndef KBLOCK
# define KBLOCK 256 /* Kill buffer block size. */
#endif
static char HUGE *kbufp = NULL; /* Kill buffer data. */
static RSIZE kused = 0; /* # of bytes used in KB. */
static RSIZE ksize = 0; /* # of bytes allocated in KB. */
static RSIZE kstart = 0; /* # of first used byte in KB. */
RSIZE return_kused()
{
return(kused - kstart);
}
/*
* This routine allocates a block of memory large enough to hold a LINE
* containing "used" characters. The block is rounded up to whatever
* needs to be allocated. (use lallocx for lines likely to grow.)
* Return a pointer to the new block, or NULL if there isn't
* any memory left. Print a message in the message line if no space.
*/
LINE *lalloc(used)
register int used;
{
register LINE *lp;
register int size;
size = used + sizeof(LINE);
#ifdef MALLOCROUND
MALLOCROUND(size); /* round up to a size optimal to malloc */
#endif
if((lp = (LINE *)calloc(1, (unsigned)size)) == NULL)
{
ewprintf(Nobytes, size);
return (LINE *)NULL;
}
lp->l_size = size - sizeof(LINE);
lp->l_used = used;
return lp;
}
/*
* Like lalloc, only round amount desired up because this line will
* probably grow. We always make room for at least one more char.
* (thus making 0 not a special case anymore.)
*/
LINE *lallocx(used)
int used;
{
register int size;
register LINE *lp;
size = (NBLOCK+used) & ~(NBLOCK-1);
if((lp = lalloc(size)) != NULL)
lp->l_used = used;
return lp;
}
/*
* Delete line "lp". Fix all of the
* links that might point at it (they are
* moved to offset 0 of the next line.
* Unlink the line from whatever buffer it
* might be in. Release the memory. The
* buffers are updated too; the magic conditions
* described in the above comments don't hold
* here.
*/
VOID lfree(lp)
register LINE *lp;
{
register BUFFER *bp;
register EWINDOW *wp;
for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
{
if (wp->w_linep == lp)
wp->w_linep = lforw(lp);
if (wp->w_dotp == lp)
{
wp->w_dotp = lforw(lp);
wp->w_doto = 0;
}
if (wp->w_markp == lp)
{
wp->w_markp = lforw(lp);
wp->w_marko = 0;
}
}
for(bp = bheadp; bp != NULL; bp = bp->b_bufp)
{
if (bp->b_nwnd == 0)
{
if (bp->b_dotp == lp)
{
bp->b_dotp = lforw(lp);
bp->b_doto = 0;
}
if (bp->b_markp == lp)
{
bp->b_markp = lforw(lp);
bp->b_marko = 0;
}
}
}
lkill(lp);
}
void lkill(lp)
LINE *lp;
{
if (!lp)
return;
if (lp->l_bp)
lp->l_bp->l_fp = lp->l_fp;
if (lp->l_fp)
lp->l_fp->l_bp = lp->l_bp;
free((char *) lp);
}
/*
* This routine gets called when
* a character is changed in place in the
* current buffer. It updates all of the required
* flags in the buffer and window system. The flag
* used is passed as an argument; if the buffer is being
* displayed in more than 1 window we change EDIT to
* HARD. Set MODE if the mode line needs to be
* updated (the "*" has to be set).
*
* NOTE special calls to changelineflag for marking the
* actual lines changed due to edit, etc. Not easy to
* combine the functions. (JAM)
*
*/
VOID lchange(flag)
register int flag;
{
register EWINDOW *wp;
if ((curbp->b_flag&BFCHG) == 0) /* First change, so */
{
curbp->b_flag |= BFCHG;
thisflag |= CFNEWC;
}
curbp->b_flag |= BFINC; /* needs incremental */
flag |= WFMODE; /* update mode lines. */
for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
{
if (wp->w_bufp == curbp)
{
wp->w_flag |= flag;
if (wp != curwp)
wp->w_flag |= WFHARD;
}
}
}
/*
* Insert "n" copies of the character "c"
* at the current location of dot. In the easy case
* all that happens is the text is stored in the line.
* In the hard case, the line has to be reallocated.
* When the window list is updated, take special
* care; I screwed it up once. You always update dot
* in the current window. You update mark, and a
* dot in another window, if it is greater than
* the place where you did the insert. Return TRUE
* if all is well, and FALSE on errors.
*/
linsert(n, c)
int n, c;
{
register char *cp1;
register char *cp2;
register LINE *lp1;
LINE *lp2;
LINE *lp3;
register int doto;
register RSIZE i;
EWINDOW *wp;
if (curbp->b_flag & BFVIEW)
{
ttbeep();
return FALSE;
}
lchange(WFEDIT);
lp1 = curwp->w_dotp; /* Current line */
if (lp1 == curbp->b_linep) /* At the end: special */
{ /* now should only happen in empty buffer */
if (curwp->w_doto != 0)
{
ewprintf("Bug!: linsert");
return FALSE;
}
if ((lp2=lallocx(n)) == NULL) /* Allocate new line */
return FALSE;
changelineflag(lp2, TRUE);
lp3 = lback(lp1); /* Previous line */
lforw(lp3) = lp2; /* Link in */
lforw(lp2) = lp1;
lback(lp1) = lp2;
lback(lp2) = lp3;
for (i=0; i<n; ++i)
lp2->l_text[i] = (char)c;
for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
{
if (wp->w_linep == lp1)
wp->w_linep = lp2;
if (wp->w_dotp == lp1)
wp->w_dotp = lp2;
if (wp->w_markp == lp1)
wp->w_markp = lp2;
}
/*NOSTRICT*/
curwp->w_doto = n;
return TRUE;
}
doto = curwp->w_doto; /* Save for later. */
/*NOSTRICT (2) */
if (lp1->l_used+n > lp1->l_size) /* Hard: reallocate */
{
if ((lp2=lallocx(lp1->l_used+n)) == NULL)
return FALSE;
changelineflag(lp2, TRUE);
cp1 = &lp1->l_text[0];
cp2 = &lp2->l_text[0];
while (cp1 != &lp1->l_text[doto])
*cp2++ = *cp1++;
/*NOSTRICT*/
cp2 += n;
while (cp1 != &lp1->l_text[lp1->l_used])
*cp2++ = *cp1++;
lp1->l_bp->l_fp = lp2;
lp2->l_fp = lp1->l_fp;
lp1->l_fp->l_bp = lp2;
lp2->l_bp = lp1->l_bp;
free((char *) lp1);
}
else
{ /* Easy: in place */
lp2 = lp1; /* Pretend new line */
changelineflag(lp2, TRUE);
/*NOSTRICT*/
lp2->l_used += n;
cp2 = &lp1->l_text[lp1->l_used];
cp1 = cp2-n;
while (cp1 != &lp1->l_text[doto])
*--cp2 = *--cp1;
}
for (i=0; i<n; ++i) /* Add the characters */
lputc(lp2, doto + i, (char)c);
for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
{
if (wp->w_linep == lp1)
wp->w_linep = lp2;
if (wp->w_dotp == lp1)
{
wp->w_dotp = lp2;
if (wp==curwp || wp->w_doto>doto)
/*NOSTRICT*/
wp->w_doto += n;
}
if (wp->w_markp == lp1)
{
wp->w_markp = lp2;
if (wp->w_marko > doto)
/*NOSTRICT*/
wp->w_marko += n;
}
}
return TRUE;
}
/*
* Insert a newline into the buffer
* at the current location of dot in the current
* window. The funny ass-backwards way is no longer used.
*/
lnewline()
{
register LINE *lp1;
register LINE *lp2;
register int doto;
register int nlen;
EWINDOW *wp;
if (curbp->b_flag & BFVIEW)
{
ttbeep();
return FALSE;
}
lchange(WFHARD);
lp1 = curwp->w_dotp; /* Get the address and */
doto = curwp->w_doto; /* offset of "." */
if(doto == 0) /* avoid unneeded copying */
{
if((lp2 = lallocx(0)) == NULL) /* new first part */
return FALSE;
changelineflag(lp2, TRUE);
lp2->l_bp = lp1->l_bp;
lp1->l_bp->l_fp = lp2;
lp2->l_fp = lp1;
lp1->l_bp = lp2;
for(wp = wheadp; wp!=NULL; wp = wp->w_wndp)
if(wp->w_linep == lp1)
wp->w_linep = lp2;
return TRUE;
}
nlen = llength(lp1) - doto; /* length of new part */
if((lp2=lallocx(nlen)) == NULL) /* New second half line */
return FALSE;
changelineflag(lp2, TRUE);
if(nlen!=0)
bcopy(&lp1->l_text[doto], &lp2->l_text[0], nlen);
if (lp1->l_used != doto)
changelineflag(lp1, TRUE);
lp1->l_used = doto;
lp2->l_bp = lp1;
lp2->l_fp = lp1->l_fp;
lp1->l_fp = lp2;
lp2->l_fp->l_bp = lp2;
for(wp = wheadp; wp != NULL; wp = wp->w_wndp) /* Windows */
{
if (wp->w_dotp == lp1 && wp->w_doto >= doto)
{
wp->w_dotp = lp2;
wp->w_doto -= doto;
}
if (wp->w_markp == lp1 && wp->w_marko >= doto)
{
wp->w_markp = lp2;
wp->w_marko -= doto;
}
}
return TRUE;
}
/*
* This function deletes "n" bytes,
* starting at dot. It understands how do deal
* with end of lines, etc. It returns TRUE if all
* of the characters were deleted, and FALSE if
* they were not (because dot ran into the end of
* the buffer. The "kflag" indicates either no insertion,
* or direction of insertion into the kill buffer.
*/
ldelete(n, kflag)
RSIZE n;
int kflag;
{
register char *cp1;
register char *cp2;
register LINE *dotp;
register int doto;
register RSIZE chunk;
EWINDOW *wp;
if (curbp->b_flag & BFVIEW)
{
ttbeep();
return FALSE;
}
/*
* HACK - doesn't matter, and fixes back-over-nl bug for empty
* kill buffers.
*/
if (kused == kstart)
kflag = KFORW;
while (n != 0)
{
dotp = curwp->w_dotp;
doto = curwp->w_doto;
if (dotp == curbp->b_linep) /* Hit end of buffer. */
return FALSE;
chunk = dotp->l_used-doto; /* Size of chunk. */
if (chunk > n)
chunk = n;
if (chunk == 0) /* End of line, merge. */
{
if(dotp == lback(curbp->b_linep))
return FALSE; /* End of buffer. */
lchange(WFHARD);
if (ldelnewline() == FALSE
|| (kflag!=KNONE && kinsert('\n', kflag)==FALSE))
return FALSE;
--n;
continue;
}
lchange(WFEDIT);
cp1 = &dotp->l_text[doto]; /* Scrunch text. */
cp2 = cp1 + chunk;
if (kflag == KFORW)
{
while (ksize - kused < chunk)
if (kgrow(FALSE) == FALSE)
return FALSE;
bcopy(cp1, &(kbufp[kused]), (size_t) chunk);
kused += chunk;
}
else if (kflag == KBACK)
{
while (kstart < chunk)
if (kgrow(TRUE) == FALSE)
return FALSE;
bcopy(cp1, &(kbufp[kstart-chunk]), (size_t) chunk);
kstart -= chunk;
}
else if (kflag != KNONE)
panic("broken ldelete call");
while (cp2 != &dotp->l_text[dotp->l_used])
*cp1++ = *cp2++;
dotp->l_used -= (int)chunk;
changelineflag(dotp, TRUE);
for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
{
if (wp->w_dotp==dotp && wp->w_doto>=doto)
{
/*NOSTRICT*/
wp->w_doto -= (short)chunk;
if (wp->w_doto < doto)
wp->w_doto = doto;
}
if (wp->w_markp==dotp && wp->w_marko>=doto)
{
/*NOSTRICT*/
wp->w_marko -= (short)chunk;
if (wp->w_marko < doto)
wp->w_marko = doto;
}
}
n -= chunk;
}
return TRUE;
}
/*
* Delete a newline. Join the current line
* with the next line. If the next line is the magic
* header line always return TRUE; merging the last line
* with the header line can be thought of as always being a
* successful operation, even if nothing is done, and this makes
* the kill buffer work "right". Easy cases can be done by
* shuffling data around. Hard cases require that lines be moved
* about in memory. Return FALSE on error and TRUE if all
* looks ok.
*/
ldelnewline()
{
register LINE *lp1;
register LINE *lp2;
register EWINDOW *wp;
LINE *lp3;
if (curbp->b_flag & BFVIEW)
{
ttbeep();
return FALSE;
}
lp1 = curwp->w_dotp;
lp2 = lforw(lp1);
if (lp2 == curbp->b_linep) /* At the buffer end. */
return TRUE;
/* Case of current line has room to just add stuff
* to the end.
*/
if (lp2->l_used <= lp1->l_size - lp1->l_used)
{
changelineflag(lp1, TRUE);
bcopy(&lp2->l_text[0], &lp1->l_text[lp1->l_used], (size_t)lp2->l_used);
for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
{
if (wp->w_linep == lp2)
wp->w_linep = lp1;
if (wp->w_dotp == lp2)
{
wp->w_dotp = lp1;
wp->w_doto += lp1->l_used;
}
if (wp->w_markp == lp2)
{
wp->w_markp = lp1;
wp->w_marko += lp1->l_used;
}
}
lp1->l_used += lp2->l_used;
lp1->l_fp = lp2->l_fp;
lp2->l_fp->l_bp = lp1;
free((char *) lp2);
return TRUE;
}
if ((lp3=lalloc(lp1->l_used + lp2->l_used)) == NULL)
return FALSE;
bcopy(&lp1->l_text[0], &lp3->l_text[0], (size_t)lp1->l_used);
bcopy(&lp2->l_text[0], &lp3->l_text[lp1->l_used], (size_t)lp2->l_used);
if ((lp1->l_used >= 1) && (lp2->l_used >= 1))
changelineflag(lp3, TRUE); /* non-empty lines joined */
lp1->l_bp->l_fp = lp3;
lp3->l_fp = lp2->l_fp;
lp2->l_fp->l_bp = lp3;
lp3->l_bp = lp1->l_bp;
for(wp = wheadp; wp != NULL; wp = wp->w_wndp)
{
if (wp->w_linep==lp1 || wp->w_linep==lp2)
wp->w_linep = lp3;
if (wp->w_dotp == lp1)
wp->w_dotp = lp3;
else if (wp->w_dotp == lp2)
{
wp->w_dotp = lp3;
wp->w_doto += lp1->l_used;
}
if (wp->w_markp == lp1)
wp->w_markp = lp3;
else if (wp->w_markp == lp2)
{
wp->w_markp = lp3;
wp->w_marko += lp1->l_used;
}
}
free((char *) lp1);
free((char *) lp2);
return TRUE;
}
/*
* Replace plen characters before dot with argument string.
* Control-J characters in st are interpreted as newlines.
* There is a casehack disable flag (normally it likes to match
* case of replacement to what was there).
*/
lreplace(plen, st, f)
register RSIZE plen; /* length to remove */
char *st; /* replacement string */
int f; /* case hack disable */
{
register RSIZE rlen; /* replacement length */
register int rtype; /* capitalization */
register int c; /* used for random characters */
register int doto; /* offset into line */
if (curbp->b_flag & BFVIEW)
{
ttbeep();
return FALSE;
}
/*
* Find the capitalization of the word that was found.
* f says use exact case of replacement string (same thing that
* happens with lowercase found), so bypass check.
*/
/*NOSTRICT*/
(VOID) backchar(FFARG | FFRAND, (int) plen);
rtype = _L;
c = lgetc(curwp->w_dotp, curwp->w_doto);
if (ISUPPER(c)!=FALSE && f==FALSE)
{
rtype = _U|_L;
if (curwp->w_doto+1 < llength(curwp->w_dotp))
{
c = lgetc(curwp->w_dotp, curwp->w_doto+1);
if (ISUPPER(c) != FALSE)
{
rtype = _U;
}
}
}
/*
* make the string lengths match (either pad the line
* so that it will fit, or scrunch out the excess).
* be careful with dot's offset.
*/
rlen = strlen(st);
doto = curwp->w_doto;
if (plen > rlen)
(VOID) ldelete((RSIZE) (plen-rlen), KNONE);
else if (plen < rlen)
{
if (linsert((int)(rlen-plen), ' ') == FALSE)
return FALSE;
}
curwp->w_doto = doto;
/*
* do the replacement: If was capital, then place first
* char as if upper, and subsequent chars as if lower.
* If inserting upper, check replacement for case.
*/
while ((c = CHARMASK(*st++)) != '\0')
{
if ((rtype&_U)!=0 && ISLOWER(c)!=0)
c = TOUPPER(c);
if (rtype == (_U|_L))
rtype = _L;
if (c == CCHR('J'))
{
if (curwp->w_doto == llength(curwp->w_dotp))
(VOID) forwchar(FFRAND, 1);
else
{
if (ldelete((RSIZE) 1, KNONE) != FALSE)
(VOID) lnewline();
}
}
else if (curwp->w_dotp == curbp->b_linep)
{
(VOID) linsert(1, c);
}
else if (curwp->w_doto == llength(curwp->w_dotp))
{
if (ldelete((RSIZE) 1, KNONE) != FALSE)
(VOID) linsert(1, c);
}
else
{
changelineflag(curwp->w_dotp, TRUE);
lputc(curwp->w_dotp, curwp->w_doto++, (char)c);
}
}
lchange(WFHARD);
return (TRUE);
}
/*
* Delete all of the text
* saved in the kill buffer. Called by commands
* when a new kill context is being created. The kill
* buffer array is released, just in case the buffer has
* grown to immense size. No errors.
*/
VOID kdelete()
{
if (kbufp != NULL)
{
ewprintf(garbage);
free((char *) kbufp);
kbufp = NULL;
kstart = kused = ksize = 0;
}
}
/*
* Insert a character to the kill buffer,
* enlarging the buffer if there isn't any room. Always
* grow the buffer in chunks, on the assumption that if you
* put something in the kill buffer you are going to put
* more stuff there too later. Return TRUE if all is
* well, and FALSE on errors. Print a message on
* errors. Dir says whether to put it at back or front.
*/
kinsert(c, dir)
int c, dir;
{
if (kused == ksize && dir == KFORW && kgrow(FALSE) == FALSE)
return FALSE;
if (kstart == 0 && dir == KBACK && kgrow(TRUE) == FALSE)
return FALSE;
if (dir == KFORW)
kbufp[kused++] = (char)c;
else if (dir == KBACK)
kbufp[--kstart] = (char)c;
else
panic("Broken kinsert call"); /* Oh shit! */
return (TRUE);
}
/*
* kgrow - just get more kill buffer for the callee. back is true if
* we are trying to get space at the beginning of the kill buffer.
*/
kgrow(back)
int back;
{
register RSIZE nstart;
register char *nbufp;
if ((unsigned)(ksize+KBLOCK) <= (unsigned)ksize)
{
/* probably 16 bit unsigned; one of these days I
* will fix this to be a linked-list of lines (JAM)
*/
ewprintf("Kill buffer size at maximum.");
return FALSE;
}
if ((nbufp=malloc((unsigned)(ksize+KBLOCK))) == NULL)
{
ewprintf(Nobytes, (long)(ksize+KBLOCK));
return FALSE;
}
nstart = (back == TRUE) ? (kstart + KBLOCK) : (KBLOCK / 4) ;
bcopy(&(kbufp[kstart]), &(nbufp[nstart]), (size_t)(kused-kstart));
if (kbufp != NULL)
free((char *) kbufp);
kbufp = nbufp;
ksize += KBLOCK;
kused = kused - kstart + nstart;
kstart = nstart;
return TRUE;
}
char *killbufstart()
{
return(&kbufp[kstart]);
}
/*
* This function gets characters from
* the kill buffer. If the character index "n" is
* off the end, it returns "-1". This lets the caller
* just scan along until it gets a "-1" back.
*/
kremove(n)
RSIZE n;
{
if (n < 0 || n + kstart >= kused)
return -1;
return CHARMASK(kbufp[n + kstart]);
}
/* insert a line at dotp of curbp
*/
int lineinsert(s, nl)
char *s;
BOOL nl;
{
while (*s)
if (!linsert(1, *s++))
return(FALSE);
if (nl)
return(lnewline());
else
return (TRUE);
}
#ifdef DEBUG
void changelineflag(lp, flag)
LINE *lp;
BOOL flag;
{
if (flag)
lp->l_flag |= LFCHANGE;
else
lp->l_flag &= ~LFCHANGE;
}
#endif